(function(global){
	if(!String.prototype.format){
		String.prototype.format = function() {
			var args = Array.prototype.slice.call(arguments);
			return this.replace(/\{(\d+)\}/g, 
				function(m, i){
					return args[i];
				});
		};
	}

	var Yo = {
		log: {}
	};

	Yo.host = global.host != null ? global.host : (function(){
		var host = 'http://tech11.cn';
		if(document.location.host == 'localhost')
			host = 'http://localhost';

		return host;
	})();

	// log api
	Yo.log.i = function(obj){
		console.log(obj);
	};

	// log api
	Yo.log.debug = function(obj){
		console.log(obj);
		if(Yo.isDebug)
			alert(obj);
	};

	// selector shortcut
	Yo.role = function(name){
		return $('[data-role="' + name + '"]');
	};

	global.Yo = Yo;
})(this);

(function(Yo){
	// easytemplate
	var easyTemplate = function(s, d) {
		if (!s) {
			return '';
		}
		if (s !== easyTemplate.template) {
			easyTemplate.template = s;
			easyTemplate.aStatement = easyTemplate
					.parsing(easyTemplate.separate(s));
		}
		var aST = easyTemplate.aStatement;
		var process = function(d2) {
			if (d2) {
				d = d2;
			}
			return arguments.callee;
		};
		process.toString = function() {
			return (new Function(aST[0], aST[1]))(d);
		};
		return process;
	};
	easyTemplate.separate = function(s) {
		var r = /\\'/g;
		var sRet = s.replace(
				/(<(\/?)#(.*?(?:\(.*?\))*)>)|(')|([\r\n\t])|(\$\{([^\}]*?)\})/g,
				function(a, b, c, d, e, f, g, h) {
					if (b) {
						return '{|}' + (c ? '-' : '+') + d + '{|}';
					}
					if (e) {
						return '\\\'';
					}
					if (f) {
						return '';
					}
					if (g) {
						return '\'+(' + h.replace(r, '\'') + ' || \'\')+\'';
					}
				});
		return sRet;
	};
	easyTemplate.parsing = function(s) {
		var mName, vName, sTmp, aTmp, sFL, sEl, aList, aStm = [ 'var aRet = [];' ];
		aList = s.split(/\{\|\}/);
		var r = /\s/;
		while (aList.length) {
			sTmp = aList.shift();
			if (!sTmp) {
				continue;
			}
			sFL = sTmp.charAt(0);
			if (sFL !== '+' && sFL !== '-') {
				sTmp = '\'' + sTmp + '\'';
				aStm.push('aRet.push(' + sTmp + ');');
				continue;
			}
			aTmp = sTmp.split(r);
			switch (aTmp[0]) {
			case '+macro':
				mName = aTmp[1];
				vName = aTmp[2];
	//			aStm.push('aRet.push("<!--' + mName + ' start--\>");');
				break;
			case '-macro':
	//			aStm.push('aRet.push("<!--' + mName + ' end--\>");');
				break;
			case '+if':
				aTmp.splice(0, 1);
				aStm.push('if' + aTmp.join(' ') + '{');
				break;
			case '+elseif':
				aTmp.splice(0, 1);
				aStm.push('}else if' + aTmp.join(' ') + '{');
				break;
			case '-if':
				aStm.push('}');
				break;
			case '+else':
				aStm.push('}else{');
				break;
			case '+list':
				aStm.push('if(' + aTmp[1] + '.constructor === Array){with({i:0,l:'
						+ aTmp[1] + '.length,' + aTmp[3] + '_index:0,' + aTmp[3]
						+ ':null}){for(i=l;i--;){' + aTmp[3] + '_index=(l-i-1);'
						+ aTmp[3] + '=' + aTmp[1] + '[' + aTmp[3] + '_index];' 
						+ 'if(!' + aTmp[3] + '){break;}');
				break;
			case '-list':
				aStm.push('}}}');
				break;
			default:
				break;
			}
		}
		aStm.push('return aRet.join("");');
		if (!vName) {
			aStm.unshift('var data = arguments[0];');
		}
		return [ vName, aStm.join('') ];
	};

	Yo.format = function(txt, data){
		return easyTemplate(txt, data).toString();
	};
})(this.Yo);

(function(Yo){
	// sessionStorage
	var Storage = function(){
		this.storage = window.sessionStorage;
		if(!this.storage)
			Yo.log.i('Web session storage api not support!');
	};

	var extend = {
		clear: function(){
			if(!this.storage)
				return;

			this.storage.clear();
		}, 

		get: function(key){
			if(!this.storage)
				return;

			var str = this.storage[key];
			if(!str || 'undefined' == str || 'null' == str)
				return null;

			return JSON.parse(str);
		}, 

		set: function(key, val){
			if(!this.storage)
				return;

			this.storage[key] = JSON.stringify(val);
		},
			
		remove: function(key){
			if(!this.storage)
				return;

			this.storage.removeItem(key);
		}
	};

	for(key in extend){
		Storage.prototype[key] = extend[key];
	}
	Yo.Storage = Storage;
})(this.Yo);

(function(Yo){
	var Utils = {};

	// cookie helper
	Utils.getCookie = function(cookieName){
		var cookieStart = document.cookie.indexOf(cookieName);
		var cookieEnd = document.cookie.indexOf(";", cookieStart);
		return cookieStart == -1 ? '' : unescape(document.cookie.substring(cookieStart + cookieName.length + 1, (cookieEnd > cookieStart ? cookieEnd : document.cookie.length)));
	};

	Utils.setCookie = function(cookieName, cookieValue, seconds, path, domain, secure) {
		var expires = new Date();
		expires.setTime(expires.getTime() + seconds);
		document.cookie = escape(cookieName) + '=' + escape(cookieValue)
			+ (expires ? '; expires=' + expires.toGMTString() : '')
			+ (path ? '; path=' + path : '/')
			+ (domain ? '; domain=' + domain : '')
			+ (secure ? '; secure' : '');
	};

	// id:1;name:xxx -> {id: 1, name: xxx}
	Utils.str2params = function(str){
		var params = {};
		if(!str)
			return params;

		_.each(str.split(';'), function(it){
			var arr = it.split(':');
			params[arr[0]] = arr[1];
		});

		return params;
	};

	// query parameters
	Utils.params = function(url){
		url = url || document.location.href;
		var data = {};
		var pos = url.indexOf('?');
		if(pos != -1){
			var query = url.substring(pos);
			if(query){
				var ll = query.substring(1).split('&');
				var i = 0;
				for(; i < ll.length; i++){
					var arr = ll[i].split('=');
					if(arr.length == 2)
						data[arr[0]] = decodeURI(arr[1]);
				}
			}
		}
		return data;
	};

	Utils.alert = function(msg){
		alert(msg);
	};

	Utils.confirm = function(msg){
		confirm(msg);
	};

	Yo.Utils = Utils;
})(this.Yo);

(function(Yo){
	// pagination
	var Pager = function(pageNum, pageSize, totalCount){
		this.pageNum = pageNum;
		this.pageSize = pageSize;
		this.totalCount = totalCount;

		this.ll = [];
		this.cb = function(){};
	};

	Pager.prototype.init = function(preBtnId, nextBtnId, cb){
		this.preBtnId = preBtnId;
		this.nextBtnId = nextBtnId;
		this.cb = cb;
		
		var that = this;
		$('#' + preBtnId).click(function(e){
			e.preventDefault();

			that.pageNum--;
			that.render();
		});

		$('#' + nextBtnId).click(function(e){
			e.preventDefault();

			that.pageNum++;
			that.render();
		});		
	};
	Pager.prototype.setList = function(ll){
		if(ll){
			this.ll = ll;
			this.totalCount = ll.length;
		}else{
			this.ll = [];
			this.totalCount = 0;
		}
	};
	Pager.prototype.getStart = function(){
		return (this.pageNum - 1) * this.pageSize;
	};
	Pager.prototype.getEnd = function(){
		return this.pageNum * this.pageSize;
	};
	Pager.prototype.hasPre = function(){
		return this.pageNum > 1;
	};
	Pager.prototype.hasNext = function(){
		return this.pageNum * this.pageSize < this.totalCount;
	};
	Pager.prototype.getTotalPageNum = function(){
		var num = Math.floor(this.totalCount / this.pageSize);
		if(this.totalCount % this.pageSize != 0)
			num++;

		return num;
	};

	Pager.prototype.render = function(){
		var subList = this.ll.slice(this.getStart(), this.getEnd());
		this.cb(subList);

		$('#' + this.preBtnId)[this.hasPre() ? 'show' : 'hide']();
		$('#' + this.nextBtnId)[this.hasNext() ? 'show' : 'hide']();
	};

	Yo.Pager = Pager;
})(this.Yo);

(function(Yo){
	var Storage = Yo.Storage;

	var Conf = {};
	Conf.get = function(key){
		var storage = new Storage();
		return storage.get(key);
	};
	Conf.set = function(key, val){
		var storage = new Storage();
		storage.set(key, val);
	};
	Conf.remove = function(key){
		var storage = new Storage();
		storage.remove(key);
	};
	Yo.Conf = Conf;
})(this.Yo);

(function(Yo){
	var Utils = Yo.Utils;
	var Conf = Yo.Conf;

	var User = {
		KEY: 'userLogin',
		COOKIE_KEY: 'yo-user-login'
	};
	User.getFromCookie = function(){
		var str = Utils.getCookie(this.COOKIE_KEY);
		if(!str)
			return null;

		return JSON.parse(JSON.parse(str));
	};
	User.get = function(){
		return Conf.get(this.KEY);
	};
	User.set = function(val){
		Conf.set(this.KEY, val);
	};
	User.remove = function(){
		Utils.setCookie(this.COOKIE_KEY, '');
		Conf.remove(this.KEY);
	};
	Yo.User = User;
})(this.Yo);

(function(Yo){
	// modals
	var Section = {
		handlers: {},
		interceptors: [],

		// load once
		flags: {}, 

		// for window.history
		his: {
			ll: []
		}, 

		addInter: function(pat, fn){
			this.interceptors.push({pat: pat, fn: fn});
		}, 

		addPageFn: function(page, fn, onlyOnce){
			this.handlers[page] = onlyOnce ? (function(key){
				return function(){
					if(!Section.flags[key]){
						fn();
						Section.flags[key] = true;
					}
				};
			})(page + '_loadOnce') : fn;
		}
	};
	Section.show = function(page){
		var modal = $('#' + page);
		modal.addClass('active');

		this.his.push('#' + page);
	};
	Section.hide = function(page){
		var modal = $('#' + page);
		modal.removeClass('active');

		this.his.remove('#' + page);
	};

	Section.go = function(page, force, params){
		if(!force){
			var i = 0;
			for(; i < this.interceptors.length; i++){
				var it = this.interceptors[i];
				if(typeof it.pat == 'object' && it.pat.constructor == RegExp){
					if(page.match(it.pat)){
						var isOk = it.fn(page);
						if(!isOk)
							return;
					}
				}
			}
		}

		this.show(page);
		var fn = this.handlers[page];
		if(fn)
			fn(params);
	};

	Section.his.getPageLast = function(){
		var pathname = document.location.pathname;
		var arr = pathname.split(/\//);
		var pathlast = arr[arr.length - 1];		

		return pathlast;
	};

	Section.his.backTo = function(page){
		var len = this.ll.length;
		var i = len - 1, one;
		for(; i >= 0; i--){
			one = this.ll[i];
			if(one.page == page){
				if('' == page){
				}else{
					var modal = $(page);
					if(!modal.hasClass('active'))
						modal.addClass('active');
				}
				one.status = '';
				break;
			}

			one.status = 'back';
			if('' == one.page){
			}else{
				var modal = $(one.page);
				if(modal.hasClass('active'))
					modal.removeClass('active');
			}
		}
	};

	Section.his.push = function(page){
		this.ll.push({page: page});

		// remove forward
		var len = this.ll.length;
		var i = len - 1, one, removedNumber = 0;
		for(; i > 0; i--){
			one = this.ll[i];
			if(one.status == 'back')
				removedNumber++;
			else
				break;
		}

		if(removedNumber){
			this.ll.slice(len - removedNumber, removedNumber);
		}

		var pathlast = this.getPageLast();
		history.pushState({page: page, type: 'modal'}, '', pathlast + page);
	};

	Section.his.last = function(){
		var len = this.ll.length;
		if(!len)
			return {page: ''};

		return this.ll[len - 1];
	};

	Section.his.remove = function(page){
		if(page != this.last().page){
			Yo.log.i('not last visited - ' + page);
			return;
		}

		this.ll.pop();
		var lastPage = this.last();

		var pathlast = this.getPageLast();
		history.replaceState({page: lastPage.page, type: 'modal'}, '', pathlast + lastPage.page);
	};

	Yo.Section = Section;
})(this.Yo);

(function(Yo){
	// http wrapper
	User = Yo.User;
	Section = Yo.Section;

	var Req = {};
	Req.get = function(url, params, cb){
		if(typeof params == 'function'){
			cb = params;
			params = {};
		}

		Pace.track(function(){
			$.get(Yo.host + url, params, function(data){
				var next = true;
				if(Req.callbackFilter){
					next = Req.callbackFilter(data);
				}
				if(next){
					cb(data);
				}
			});
		});
	};
	Req.post = function(url, data, cb){
		Pace.track(function(){
			$.ajax({
				type: 'POST', 
				url: Yo.host + url, 
				data: JSON.stringify(data), 
				dataType: 'json', 
				contentType: 'application/json',
				success: function(data){
					var next = true;
					if(Req.callbackFilter){
						next = Req.callbackFilter(data);
					}
					if(next){
						cb(data);
					}

//					if(data && data.error){
//						if('session-invalid' == data.error){
//							User.remove();
//							Section.show('user-login');
//							return;
//						}else{
//							alert(data.error);
//						}
//					}else{
//						cb(data);
//					}
				}, 
				error: function(xhr, errorType, error){
					console.error(error);
				}
			});
		});
	};

	Yo.Req = Req;
})(this.Yo);